package sql

import (
	"os"
	"strings"
	"sync"

	_ "github.com/denisenkom/go-mssqldb"
	"github.com/giter/go.rice"
	_ "github.com/go-sql-driver/mysql"

	"github.com/chinahdkj/xorm"
	"github.com/xormplus/core"
	"gitee.com/dayu1985/framework/context"
	"gitee.com/dayu1985/framework/logging"
	//_ "github.com/lunny/godbc"
)

var mutex = sync.Mutex{}
var xorms = map[string]*xorm.Engine{}

func DefaultXorm(c *context.Configuration) *xorm.Engine {
	return MustXorm(c, context.SECTION_DATABASE)
}

func MustXorm(c *context.Configuration, section string) *xorm.Engine {

	mutex.Lock()
	defer mutex.Unlock()

	if xorms[section] != nil {
		return xorms[section]
	}

	mode := c.INI.MustValue(section, "mode", "")
	showSQL := c.INI.MustBool(section, "db.sql", false)
	uri := c.INI.MustValue(section, "db.uri", "")

	engine, err := xorm.NewEngine(mode, uri)

	if err != nil {
		panic(err)
	}

	db_prefix := c.INI.MustValue(section, "db.prefix", "")

	if db_prefix != "" {
		engine.SetTableMapper(core.NewPrefixMapper(core.SnakeMapper{}, db_prefix))
	}

	//注意， 必须先调用一次否则后续的添加会错误
	engine.Dialect().Filters()

	switch engine.Dialect().DBType() {
	case core.ORACLE:
		engine.SetTableMapper(UpperMapper{Mapper: engine.GetTableMapper()})
		engine.SetColumnMapper(UpperMapper{Mapper: core.SnakeMapper{}})
		engine.Dialect().(xorm.MutableFilter).AddFilter(&OracleUpperFilter{})
	case core.MSSQL:
		engine.Dialect().(xorm.MutableFilter).AddFilter(&MSSQLFilter{})
	}

	maxIdle := c.INI.MustInt(section, "db.max-idle", 0)
	if maxIdle > 0 {
		engine.SetMaxIdleConns(maxIdle)
	}

	maxOpenConns := c.INI.MustInt(section, "db.max-open-conns", 0)
	if maxOpenConns > 0 {
		engine.SetMaxOpenConns(maxOpenConns)
	}

	if err != nil {
		panic(err)
	}

	engine.ShowSQL(showSQL)

	if showSQL {
		engine.ShowExecTime(true)
	}

	xorms[section] = engine
	return engine
}

func SetSqlPath(engine *xorm.Engine, box *rice.Box, dirs ...string) {

	suffix := ".sql.tpl"

	engine.RegisterSqlTemplate(xorm.Pongo2(box.Name(), suffix))

	if len(dirs) == 0 {
		dirs = []string{""}
	}

	for _, dir := range dirs {

		prefix := "/" + dir

		box.Walk(prefix, func(path string, info os.FileInfo, err error) error {

			if err != nil {
				return err
			}

			if info.IsDir() {
				return nil
			}

			path = strings.TrimPrefix(path, "/")
			path = strings.Replace(path, "\\", "/", -1)

			str := box.MustString(path)
			key := strings.TrimSuffix(path, suffix)
			key = strings.TrimPrefix(key, dir)
			key = strings.TrimPrefix(key, "/")

			logging.Info("add sql %s...", key)
			err = engine.AddSqlTemplate(key, str)

			if err != nil {
				panic(err.Error())
			}

			return err
		})
	}
}

type UpperMapper struct {
	Mapper core.IMapper
}

func (mapper UpperMapper) Obj2Table(name string) string {

	if mapper.Mapper != nil {
		name = mapper.Mapper.Obj2Table(name)
	}

	return strings.ToUpper(name)
}

func (mapper UpperMapper) Table2Obj(name string) string {

	if mapper.Mapper != nil {
		return mapper.Mapper.Table2Obj(name)
	}

	return name
}
